use super::*;

/// Result type alias for N-API operations
pub type Result<'env, T> = core::result::Result<T, Status<'env>>;

/// Trait for throwing errors to JavaScript
pub trait NapiThrow {
	type Output;
	/// Throws the error to JavaScript environment
	fn throw(self, _: &'_ Env) -> Option<Self::Output>;
}

impl<T, E: IntoNapi + core::error::Error> NapiThrow for core::result::Result<T, E> {
	type Output = T;
	fn throw(self, env: &'_ Env) -> Option<Self::Output> {
		match self {
			Ok(v) => Some(v),
			Err(e) => {
				log::error!("throw err {}", e);
				unsafe { sys::napi_throw(env.raw(), e.into_napi(env).unwrap().raw()) };
				None
			}
		}
	}
}

/// Extension trait for napi_status error handling
pub trait NapiStatusExt {
	/// Converts napi_status to Result with normal error type
	fn result<'env>(self, _: &'env str) -> Result<'env, ()>;
	/// Converts napi_status to Result with type error type
	fn result_type<'env>(self, msg: &'env str) -> Result<'env, ()>;
	/// Converts napi_status to Result with range error type
	fn result_range<'env>(self, msg: &'env str) -> Result<'env, ()>;
}

impl NapiStatusExt for sys::napi_status {
	fn result<'env>(self, msg: &'env str) -> Result<'env, ()> {
		match self {
			sys::napi_ok => Ok(()),
			v => Err(Status(v, msg, ErrorType::Normal)),
		}
	}
	fn result_type<'env>(self, msg: &'env str) -> Result<'env, ()> {
		match self {
			sys::napi_ok => Ok(()),
			v => Err(Status(v, msg, ErrorType::Type)),
		}
	}
	fn result_range<'env>(self, msg: &'env str) -> Result<'env, ()> {
		match self {
			sys::napi_ok => Ok(()),
			v => Err(Status(v, msg, ErrorType::Range)),
		}
	}
}

/// Represents a N-API error status
#[derive(Debug, Clone, Copy)]
pub struct Status<'val>(pub sys::napi_status, pub &'val str, pub ErrorType);

impl<'env> core::error::Error for Status<'env> {}
impl<'env> core::fmt::Display for Status<'env> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		core::fmt::Debug::fmt(&self, f)
	}
}

impl<'val> Status<'val> {
	/// Creates an invalid argument error
	pub fn invalid_arg<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_invalid_arg, msg, ErrorType::Normal)
	}
	/// Creates a generic failure error
	pub fn generic_failure<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_generic_failure, msg, ErrorType::Normal)
	}
	/// Creates a cancelled operation error
	pub fn cancelled<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_cancelled, msg, ErrorType::Normal)
	}
	/// Creates an object expected error
	pub fn object_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_object_expected, msg, ErrorType::Normal)
	}
	/// Creates a string expected error
	pub fn string_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_string_expected, msg, ErrorType::Normal)
	}
	/// Creates a name expected error
	pub fn name_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_name_expected, msg, ErrorType::Normal)
	}
	/// Creates a function expected error
	pub fn function_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_function_expected, msg, ErrorType::Normal)
	}
	/// Creates a number expected error
	pub fn number_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_number_expected, msg, ErrorType::Normal)
	}
	/// Creates a boolean expected error
	pub fn boolean_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_boolean_expected, msg, ErrorType::Normal)
	}
	/// Creates an array expected error
	pub fn array_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_array_expected, msg, ErrorType::Normal)
	}
	/// Creates a pending exception error
	pub fn pending_exception<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_pending_exception, msg, ErrorType::Normal)
	}
	/// Creates an escape called twice error
	pub fn escape_called_twice<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_escape_called_twice, msg, ErrorType::Normal)
	}
	/// Creates a handle scope mismatch error
	pub fn handle_scope_mismatch<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_handle_scope_mismatch, msg, ErrorType::Normal)
	}
	/// Creates a callback scope mismatch error
	pub fn callback_scope_mismatch<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_callback_scope_mismatch, msg, ErrorType::Normal)
	}
	/// Creates a queue full error
	pub fn queue_full<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_queue_full, msg, ErrorType::Normal)
	}
	/// Creates a closing error
	pub fn closing<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_closing, msg, ErrorType::Normal)
	}
	/// Creates a bigint expected error
	pub fn bigint_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_bigint_expected, msg, ErrorType::Normal)
	}
	/// Creates a date expected error
	pub fn date_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_date_expected, msg, ErrorType::Normal)
	}
	/// Creates an arraybuffer expected error
	pub fn arraybuffer_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_arraybuffer_expected, msg, ErrorType::Normal)
	}
	/// Creates a detachable arraybuffer expected error
	pub fn detachable_arraybuffer_expected<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_detachable_arraybuffer_expected, msg, ErrorType::Normal)
	}
	/// Creates a would deadlock error
	pub fn would_deadlock<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_would_deadlock, msg, ErrorType::Normal)
	}
	/// Creates a create ark runtime too many envs error
	pub fn create_ark_runtime_too_many_envs<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_create_ark_runtime_too_many_envs, msg, ErrorType::Normal)
	}
	/// Creates a create ark runtime only one env per thread error
	pub fn create_ark_runtime_only_one_env_per_thread<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_create_ark_runtime_only_one_env_per_thread, msg, ErrorType::Normal)
	}
	/// Creates a destroy ark runtime env not exist error
	pub fn destroy_ark_runtime_env_not_exist<'env>(msg: &'env str) -> Status<'env> {
		Status(sys::napi_destroy_ark_runtime_env_not_exist, msg, ErrorType::Normal)
	}
}

impl<'val> IntoNapi for Status<'val> {
	/// Converts Status to JavaScript Error object
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		let code = self.0.into_napi(env)?;
		let msg = self.1.into_napi(env)?;
		Ok(Error::new(env, self.2, code, msg)?.v())
	}
}
